// Definitions related to RISC-V 64 instructions
#ifndef __RV64_H__
#define __RV64_H__
#include <cstdint>

enum class RV64GFR {
	// 32 General purpose registers
	ZERO, RA, SP, GP, TP, T0, T1, T2,
	/*
	 * NOTE: the order of argument regs should NOT
	 * be modified because they are used in determining
	 * augument passing!
	 */
	S0, S1, A0, A1, A2, A3, A4, A5, A6, A7,
	S2, S3, S4, S5, S6, S7, S8, S9, S10, S11,
	T3, T4, T5, T6,
	NGPR,
	// 32 Floating-point registers
	FT0 = NGPR, FT1, FT2, FT3, FT4, FT5, FT6, FT7,
	FS0, FS1, FA0, FA1, FA2, FA3, FA4, FA5, FA6, FA7,
	FS2, FS3, FS4, FS5, FS6, FS7, FS8, FS9, FS10, FS11,
	FT8, FT9, FT10, FT11,
	// Total number
	NGFR
};
// size of GPR/FPR
#define RV64_GFR_SIZE 8u
// alignment of `$sp`
#define RV64_SP_ALIGN 16u
// number of GPRs/FPRs to pass arguments
#define RV64_AGPR 8u
#define RV64_AFPR 8u

// RV64 instructions operation
enum class RV64Oper {
/* ---------- Base integer ---------- */
	// NOP
	NOP,
	// R Type arithmetic and logic
	ADD, SUB, AND, OR, XOR, SLL, SRA, SRL, //NOT,
	// I Type, and shift
	ADDi, ANDi, ORi, XORi, SLLi, SRAi, SRLi,
	// Compare
	SLT, SLTu, SLTi, SLTiu,
	// Memory access
	LOAD, STORE,
	// Load address and load symbol pseudoinstructions
	LA, LDs,
	// Load immediate
	LI,
	// Function call and return pseudoinstructions
	CALL, RET,
	// Control transfer and related pseudoinstrctions
	J, BEQ, BNE, BLT, BLTu, BGE, BGEu, BLE, BLEu, BGT, BGTu,
	// Move
	MV,
/* ---------- M extension --------- */
	// Multiplication and division
	MUL, DIV, REM,
/* ---------- F&D extension ---------- */
	// Memory access
	FLOAD, FSTORE,
	// Arithmetic
	FADD, FSUB, FMUL, FDIV, FMIN, FMAX,
	// Move (between FPRs) and related pseudoinstruction
	FMV, FNEG, FABS,
	// Move from FPR to GPR, and from GPR to FPR
	FMV2I, FMV4I,
	// Compare and related pseudoinstrctions
	FLT, FLE, FGT, FGE, FEQ,
/* ---------- Other ---------- */
	LABEL
};

// Operation size of an instruction, both for int and float
enum class RV64Opsz {DEFAULT = 0, B = 1, H = 2, W = 3, S = 4, D = 5};

/*
 * Return whether a GPR/FPR should be saved by callee.
 * NOTE: although `$ra` is not callee-saved in ABI,
 * it also should be saved if we use it.
 */
static inline bool RV64GFR_is_callee_saved(RV64GFR reg)
{
	return reg == RV64GFR::RA || (
		reg == RV64GFR::SP || reg == RV64GFR::S0 || reg == RV64GFR::S1
		|| reg == RV64GFR::S2 || reg == RV64GFR::S3 || reg == RV64GFR::S4
		|| reg == RV64GFR::S5 || reg == RV64GFR::S6 || reg == RV64GFR::S7
		|| reg == RV64GFR::S8 || reg == RV64GFR::S9 || reg == RV64GFR::S10
		|| reg == RV64GFR::S11
	) || (
		reg == RV64GFR::FS0 || reg == RV64GFR::FS1 || reg == RV64GFR::FS2
		|| reg == RV64GFR::FS3 || reg == RV64GFR::FS4 || reg == RV64GFR::FS5
		|| reg == RV64GFR::FS6 || reg == RV64GFR::FS7 || reg == RV64GFR::FS8
		|| reg == RV64GFR::FS9 || reg == RV64GFR::FS10 || reg == RV64GFR::FS11
	);
}

static inline bool RV64GFR_is_GPR(RV64GFR reg)
{
	return (unsigned(reg) < unsigned(RV64GFR::NGPR));
}

// list all regs that MAY be modified after a function call
const RV64GFR RV64GFR_caller_saved[] = {
	RV64GFR::RA, RV64GFR::T0, RV64GFR::T1, RV64GFR::T2, RV64GFR::T3,
	RV64GFR::T4, RV64GFR::T5, RV64GFR::T6, RV64GFR::A0, RV64GFR::A1,
	RV64GFR::A2, RV64GFR::A3, RV64GFR::A4, RV64GFR::A5, RV64GFR::A6,
	RV64GFR::A7,
	RV64GFR::FT0, RV64GFR::FT1, RV64GFR::FT2, RV64GFR::FT3, RV64GFR::FT4,
	RV64GFR::FT5, RV64GFR::FT6, RV64GFR::FT7, RV64GFR::FT8, RV64GFR::FT9,
	RV64GFR::FT10, RV64GFR::FT11, RV64GFR::FA0, RV64GFR::FA1, RV64GFR::FA2,
	RV64GFR::FA3, RV64GFR::FA4, RV64GFR::FA5, RV64GFR::FA6, RV64GFR::FA7
};

#endif